/* ************************************************************************* */
/*                                                                           */
/*  Open the file whose name is in SFReplyRec. If the call is good, save     */
/*  the refnum, and put the file into NewLine mode. In this mode, ProDOS     */
/*  will read characters into a buffer until a certain character is read.    */
/*  In this case, we will read characters until a carriage return is read.   */
/*                                                                           */
/* ************************************************************************* */

int OpenFile()
{
    if (myReply.good) {         /* Check to see if we have a valid record   */
        OPEN(&OpenParms);       /* Open the file                            */
        if (OpenParms.openRefNum != 0) {    /* Put it into NewLine mode     */
            NewlineParms.newLRefNum = OpenParms.openRefNum;
            IOParms.fileRefNum = OpenParms.openRefNum;
            NEWLINE(&NewlineParms);
        }
        return(OpenParms.openRefNum);   /* return refnum for opened file    */
    } else {
        return(0);              /* If record is bad, return 0 for refnum    */
    }
}


/* ************************************************************************* */
/*                                                                           */
/*  Read a line from the file that we opened. Tack on a NULL at the end of   */
/*  line to appease C, and return the number characters read.                */
/*                                                                           */
/* ************************************************************************* */

int ReadFile()
{
    READ(&IOParms);
    line[IOParms.transferCount] = '\0';
    return(IOParms.transferCount);
}


/* ************************************************************************* */
/*                                                                           */
/*  Close the file that we are reading.                                      */
/*                                                                           */
/* ************************************************************************* */

void CloseFile()
{
    CLOSE(&IOParms);
}


/* ************************************************************************* */
/*                                                                           */
/*  Buffers for the routines that print the Header and Footer.               */
/*                                                                           */
/* ************************************************************************* */

char HeaderFooter[255];
char BuiltHF[255];


/* ************************************************************************* */
/*                                                                           */
/*  Handy formula that takes a column number and returns the coordinate of   */
/*  left margin for that column.                                             */
/*                                                                           */
/* ************************************************************************* */

int SetMargin(col)
int col;
{
    return(((*myPagePtr).h2-(*myPagePtr).h1)*col / numCols + (*myPagePtr).h1);
}


/* ************************************************************************* */
/*                                                                           */
/*  This routine take the string that is stored in the HeaderFooter string   */
/*  buffers, and starts parsing it, starting at the character indexed by the */
/*  variable 'src'. It will read up until the ned of the string or the next  */
/*  '|', parsing embedded commands, and placing the resulting string into    */
/*  'BuiltHF'.                                                               */
/*                                                                           */
/*  Special substitutions ('@' marks embedded command):
/*      D:  replace with current date.
/*      T:  replace with current time.
/*      L:  replace with full path name.
/*      S:  replace with file name (short pathname).
/*      P:  replace with page number.
/*                                                                           */
/* ************************************************************************* */

int GetLineSegment(src)
int src;
{
    int   dest,i,max,sLen;
    char    pageText[3];

    dest = 0;
    sLen = (int) HeaderFooter[0];

    while ((HeaderFooter[src] != '|') && (src <= sLen)) {
        if (HeaderFooter[src] == '@') {
            src++;                              /* Skip over the '@'        */
            switch (HeaderFooter[src++]) {      /* skips over format code   */
                case 'd':
                case 'D':
                    for (i=0;i<=7; BuiltHF[dest++] = (time[i++] & 0x7f));
                    break;
                case 't':
                case 'T':
                    max = ReadBParam(0x35) ? 16 : 19;
                    for (i=9; i<=max; BuiltHF[dest++] = (time[i++] & 0x7f));
                    break;
                case 'l':
                case 'L':
                    max = (int) myReply.fullPathname[0];
                    for (i=1; i<=max; BuiltHF[dest++] =
                                        myReply.fullPathname[i++]);
                    break;
                case 's':
                case 'S':
                    max = (int) myReply.filename[0];
                    for (i=1; i<=max; BuiltHF[dest++] = myReply.filename[i++]);
                    break;
                case 'p':
                case 'P':
                    Int2Dec(pageNum,pageText,3,false);
                    for (i=0; i<=2; BuiltHF[dest++] = pageText[i++]);
                    break;
                default:
                    src -= 2;       /* Go back to the '@' and copy it       */
                    BuiltHF[dest++] = HeaderFooter[src++];
                    /* end up pointing to ex-format code, and printing it   */
                    break;
            }  /* end switch */
        } else {
            BuiltHF[dest++] = HeaderFooter[src++];
        }  /* == '@' */
    }       /* == '|' */
    BuiltHF[dest] = '\0'; /* replace the '|' with a NULL, ending the string */
    return(++src);      /* skip over the '|' */
}


/* ************************************************************************* */
/*                                                                           */
/*  If we are printing in PostScript mode, we need to fix up the string. The */
/*  characters "/", "(", and ")" are special to PostScript. In order to      */
/*  print them, we need to stick a "\" in front of them.                     */
/*                                                                           */
/* ************************************************************************* */

void FixString(string)
char string[];
{
    char strCopy[255];
    char *srcPtr,*destPtr;
    char ch;

    srcPtr = string;            /* Set up pointers to the source string and */
    destPtr = strCopy;          /* to the buffer where a copy of it will go */

    while ( *destPtr++ = *srcPtr++);    /* Make a copy of the string        */

    srcPtr = strCopy;                   /* reset our pointers               */
    destPtr = string;

    while ((ch = *srcPtr++) != 0) {     /* Now fix all of the characters    */
        if ((ch == '\\') || (ch == '(') || (ch == ')')) {
            *destPtr++ = '\\';
        }
        if (ch != 0x0d) *destPtr++ = ch;    /* and remove carriage returns! */
    }
    *destPtr = '\0';                    /* make this thing a C String       */
}

/* ************************************************************************* */
/*                                                                           */
/*  Sends the specified string out at the specified location. This routine   */
/*  determines if we are in PostScript mode, and will create a PostScript    */
/*  command string if so. If not, it will simply use QuickDraw to draw the   */
/*  string.                                                                  */
/*                                                                           */
/* ************************************************************************* */

void SendString(line,x,y)
char    line[];
int     x,y;
{
    char    xStr[6],yStr[6],printStr[255];

    if (PostScript) {
        FixString(line);            /* Escape out all '\', '(', and ')'     */
        Int2Dec(x,xStr,5,true);     /* Put XY coordinates into ASCII        */
        Int2Dec(y,yStr,5,true);
        xStr[5] = '\0';             /* Int2Dec just returns the ASCII chars */
        yStr[5] = '\0';             /* We need to turn them into C Strings  */
        strcpy(printStr,xStr);      /* Build a moveTo PostScript command    */
        strcat(printStr,yStr);
        strcat(printStr,PSMoveStr);
        strcat(printStr,PSShow1);
        strcat(printStr,line);      /* Catenate the string to print.        */
        strcat(printStr,PSShow2);   /* Catenate a 'show' command.           */
        DrawCString(printStr);      /* Send it to LaserWriter driver.       */
    } else {
        MoveTo(x,y);                /* Use QuickDraw to position and        */
        DrawCString(line);          /* display the string.                  */
    }
}


/* ************************************************************************* */
/*                                                                           */
/*  Takes a string assumed to be a header or footer, and prints it where     */
/*  it is told to. This routine gets the current time, and then calls        */
/*  GetLineSegment() to get part of the Header/Footer and parse out the      */
/*  special codes in it. Then it calls SendString to print it out. It does   */
/*  this 3 times for the 3 possible segments of the Header/Footer.           */
/*                                                                           */
/* ************************************************************************* */

void DrawHeaderFooter(v,left,right)
int v,left,right;
{
    int     src,width;

    ReadAsciiTime(time);            /* Get the current time for parsing     */
    src = 1;                        /* Start parsing the string at the start */
    src = GetLineSegment(src);      /* Get a parsed line segment. Set src.  */
    SendString(BuiltHF,left+10,v);  /* Print it in the left slot            */

    src = GetLineSegment(src);      /* Get another segment where we left off */
    width = CStringWidth(BuiltHF);  /* Find width in order to center it     */
    SendString(BuiltHF,(right+left-width)/2,v); /* Print it centered        */

    GetLineSegment(src);            /* Get the last segment.                */
    width = CStringWidth(BuiltHF);  /* Get width to right justify it        */
    SendString(BuiltHF,right-10-width,v);       /* and print it.            */
}


/* ************************************************************************* */
/*                                                                           */
/*  Read the Header string from the EditLine item, and call upon             */
/*  DrawHeaderFooter() to format it, position it, and print it.              */
/*                                                                           */
/* ************************************************************************* */

void PrintHeader(column)
int column;
{
    int row;

    GetIText(myDialog,Header,HeaderFooter);
    row = (*myPagePtr).v1 + lineHeight + lineHeight/2;
    DrawHeaderFooter(row,SetMargin(column),SetMargin(column+1));
}


/* ************************************************************************* */
/*                                                                           */
/*  Read the Footer string from the EditLine item, and call upon             */
/*  DrawHeaderFooter() to format it, position it, and print it.              */
/*                                                                           */
/* ************************************************************************* */

void PrintFooter(column)
int column;
{
    int row;

    GetIText(myDialog,Footer,HeaderFooter);
    row = (*myPagePtr).v2 - lineHeight + lineHeight/2;
    DrawHeaderFooter(row,SetMargin(column),SetMargin(column+1));
}


/* ************************************************************************* */
/*                                                                           */
/*  Draw any borders needed. If the Global "Borders Needed" flag             */
/*  (myBorders[0].used) is FALSE, then do nothing. Else, check the flags for */
/*  each part of the borders array, and print the border line if appropriate */
/*                                                                           */
/* ************************************************************************* */

void DrawPageBorders()
{
    int     v1,v2,h1,h2;
    int     margin,column;

    v1 = (*myPagePtr).v1;
    h1 = (*myPagePtr).h1;
    v2 = (*myPagePtr).v2;
    h2 = (*myPagePtr).h2;

    if (myBorders[0].used) {
        if (myBorders[1].used) {
            MoveTo(h1,v1);
            LineTo(h2,v1);
        }
        if (myBorders[2].used) {
            MoveTo(h1,v1+lineHeight*2);
            LineTo(h2,v1+lineHeight*2);
        }
        if (myBorders[3].used) {
            MoveTo(h1,v2-lineHeight*2);
            LineTo(h2,v2-lineHeight*2);
        }
        if (myBorders[4].used) {
            MoveTo(h1,v2);
            LineTo(h2,v2);
        }
        if (myBorders[5].used) {
            MoveTo(h1,v1);
            LineTo(h1,v2);
        }
        if (myBorders[7].used) {
            MoveTo(h2,v1);
            LineTo(h2,v2);
        }
        if ((myBorders[6].used) && (numCols > 1)) {
            for (column = 1; column < numCols; column++) {
                margin = SetMargin(column);
                MoveTo(margin,v1);
                LineTo(margin,v2);
            } /* end for */
        } /* end if myBorders[6] etc. */
    } /* end of "if any borders are used at all" */
}


/* ************************************************************************* */
/*                                                                           */
/*  Print a single column. It prints any headers and/or footers, and then    */
/*  loops through the number of lines printable on the page. For every time  */
/*  through the loop, it reads a line from the file, and calls SendString    */
/*  to print it in thr right location and right mode (PostScript or Quick-   */
/*  Draw II). If we reached the end of the file, return this fact to         */
/*  PrintAPage().                                                            */
/*                                                                           */
/* ************************************************************************* */

int PrintAColumn(column)
int column;
{
    int lineNum,margin;
    boolean done;

    if (haveHeader) PrintHeader(column);
    if (haveFooter) PrintFooter(column);
    lineNum = 1;
    margin = SetMargin(column);
    do {
        done = (ReadFile() == 0);
        if (!done) SendString(line,margin+10,lineNum*lineHeight+lineTop);
    } while ((!done) && (lineNum++ < maxLines));
    return (done);
}


/* ************************************************************************* */
/*                                                                           */
/*  Called by the heart of the Print Loop to print a single page. This       */
/*  routine draws the borders, sets up the printer for PostScript input      */
/*  if it is a LaserWriter, prints 'numCols' number of columns, and then     */
/*  turns off PostScript mode if appropriate. This routine also detects if   */
/*  we reached the end of the file or not, and sends that bit of information */
/*  back to the Print Loop to tell it to stop when it's time.                */
/*                                                                           */
/* ************************************************************************* */

boolean PrintAPage()
{
    int     column, prErrorSave;
    boolean done;

    column = 0;
    MoveTo(20,20);                          /* force a font to be set       */
    DrawChar(' ');
    DrawPageBorders();

    if (PostScript) {
        PicComment(PostScriptBegin,0,NIL);  /* Turn on PostScripting        */
        PicComment(TextIsPostScript,0,NIL); /* Set form of PostScripting    */
    }

    do {                                    /* Print x number of columns    */
        done = PrintAColumn(column);
        column++;
        pageNum++;
    } while ((!done) && (column < numCols));

    if (PostScript) {

        /* If an error occurred, PrError() would cause all Print Manager    */
        /* routines to short-circuit themselves. If so, then we won't be    */
        /* able to turn off PostScripting! So we have to save the current   */
        /* value of PrError(), set it to zero, turn off PostScripting, and  */
        /* then set PrError() back to its old value. NOTE: This is only a   */
        /* problem with System Disk 3.2; the Print Manager on System Disk   */
        /* 4.0 clears PostScript mode on PrClosePage().                     */

        prErrorSave = PrError();
        PrSetError(0);
        PicComment(PostScriptEnd,0,NIL);    /* Turn off PostScripting       */
        PrSetError(prErrorSave);
    }

    return(done);   /* Tell Print Loop if this was the Last Page or not     */
}


/* ************************************************************************* */
/*                                                                           */
/*  Routine called in response to clicking on the Print button. It sets up   */
/*  some variables, and then goes into the Classic Print Loop:               */
/*                                                                           */
/*      Display Job Dialog box
/*      Open the Document
/*          Open the Page
/*              Install my font
/*              Print the Page
/*          Close the Page
/*      Until all pages are printed
/*      UnSpool the picture if Printing to an ImageWriter
/*                                                                           */
/* ************************************************************************* */

void doPrint()
{
    boolean         done;
    GrafPortPtr     myPort;
    int             refNum;
    FontInfoRecord  theInfoRec;
    PrStatusRec     myStatusRec;
    GrafPortPtr     NowPrintingDialogPtr;
    FontHndl        oldFont;

    /* Get some last minute information, and give user a chance to Cancel   */

    if (PrJobDialog(PrintHndl)) {

        /* Got the OK. Show a nifty status box.                             */

        NowPrintingDialogPtr = GetNewModalDialog(&NowPrintingDialog);
        BeginUpdate(NowPrintingDialogPtr);
        DrawDialog(NowPrintingDialogPtr);
        EndUpdate(NowPrintingDialogPtr);

        /* See if we a printing to a LaserWriter.                           */

        PostScript = ((**PrintHndl).prInfo.iDev == 3);


        /* Set up our own logical page size. This lets me set some margin   */
        /* space for 3-hole punches                                         */

        (**PrintHndl).prInfo.rPage.v1 += (sideways?50:5);
        (**PrintHndl).prInfo.rPage.h1 += (sideways?5:50);
        (**PrintHndl).prInfo.rPage.v2 -= 5;
        (**PrintHndl).prInfo.rPage.h2 -= 5;

        oldFont = GetFont();
        InstallFont(theFont,0); /* do this once so we can get info on it    */
        GetFontInfo(&theInfoRec);
        SetFont(oldFont);               /* Set us back to the System Font   */
        lineHeight = theInfoRec.ascent
                    + theInfoRec.descent
                    + theInfoRec.leading;


        /* We need to kludge something here. Although we set up rPage       */
        /* above, PrOpendoc and PrOpenPage reset it! So we make a local     */
        /* copy here, and use IT in all of our calculations.                */

        myPage = (**PrintHndl).prInfo.rPage;
        myPagePtr =  &myPage;


        /* Initialize the page number counter                               */
        pageNum = 1;


        /* Set up variables used to determine where to start and stop       */
        /* printing on the page.                                            */
        /*                                                                  */
        /* Step One: assume that there are no borders, headers, or          */
        /* footers.                                                         */

        lineTop =  (*myPagePtr).v1;
        lineBottom = (*myPagePtr).v2;


        /* Step Two: See what types of borders we are using, and            */
        /* adjust the top and bottom margins accordingly.                   */

        if (myBorders[0].used) {
            if (myBorders[1].used)
                lineTop = (*myPagePtr).v1 + lineHeight;
            if (myBorders[2].used)
                lineTop = (*myPagePtr).v1 + lineHeight*3;
            if (myBorders[4].used)
                lineBottom = (*myPagePtr).v2 - lineHeight;
            if (myBorders[3].used)
                lineBottom = (*myPagePtr).v2 - lineHeight*3;
        }


        /* Step Three: If we are using any Headers or Footers, set          */
        /* aside space for them.                                            */

        GetIText(myDialog,Header,HeaderFooter);
        haveHeader = ((int)*HeaderFooter > 0);
        GetIText(myDialog,Footer,HeaderFooter);
        haveFooter = ((int)*HeaderFooter > 0);

        if (haveHeader) lineTop = (*myPagePtr).v1 + lineHeight*3;
        if (haveFooter) lineBottom = (*myPagePtr).v2 - lineHeight*3;


        /* Figure out how many lines that comes out to.                     */
        maxLines = (lineBottom-lineTop) / lineHeight;


        /* Attempt to open the file. If that succeeds, then go into the     */
        /* Printing loop.                                                   */

        if (OpenFile() != 0) {

            BeginUpdate(myDialog);  /* update the window first              */
            DrawDialog(myDialog);
            EndUpdate(myDialog);

            /* We now enter into the Classical Print Loop. Notice the       */
            /* gyrations I go through in the middle with the checking of    */
            /* PrError(). Normally, you wouldn't think that this would be   */
            /* necessary, as the Print Manager will automatically skip over */
            /* the rest of the printing if an error occurs. However, I have */
            /* to make my routine more intelligent for 2 reasons:           */
            /*      1) I can abort the reading of the rest of the file if   */
            /*          I know that an error has occured. Even though the   */
            /*          Print manager won't print anything if an error      */
            /*          occurs, I can speed things up by doing this.        */
            /*      2) The Print Manager short-circuits all of its routines */
            /*          when an error occurs. It makes a check of PrError() */
            /*          at the beginning of all routines and if it is not   */
            /*          equal to zero, it returns before doing anything     */
            /*          else. However, assume the case where the user       */
            /*          presses Cmd-. before PrOpenPage() is called for the */
            /*          time. Among other things, PrOpenPage() is respon-   */
            /*          sible for setting the port to the Printer Port      */
            /*          ('myPort' as listed below). If Cmd-. is pressed,    */
            /*          PrOpenPage() will not do that, and the port is left */
            /*          set to my Dialog Window. If I were to then perform  */
            /*          my InstallFont(), I would set the font of the       */
            /*          Dialog Window, and not the Printer Port, like I     */
            /*          wanted. So I have to make a check of PrError to see */
            /*          if it is OK to proceed.                             */

            myPort = PrOpenDoc(PrintHndl,NULL); /* Start a print session    */
            if (_toolErr != 0) PrSetError(_toolErr);
            do {
                PrOpenPage(myPort, NULL);   /* Open port for printing into. */
                if (_toolErr != 0) PrSetError(_toolErr);
                if (PrError() != 0) {       /* If an error occurs...        */
                    done = TRUE;            /* ... get outta here!          */
                } else {                    /* If no error occurs...        */
                    InstallFont(theFont,0); /* ...reset the font...         */
                    done = PrintAPage();    /* ...and print a page.         */
                }
                PrClosePage(myPort);        /* Close the printer's GrafPort */
                if (_toolErr != 0) PrSetError(_toolErr);
            } while (!done);                /* "Bop til you drop!"          */
            PrCloseDoc(myPort);             /* Close this print session.    */
            if (_toolErr != 0) PrSetError(_toolErr);

            SetPort(myDialog);              /* Set port back to Dialog box. */
            CloseFile();                    /* Close the file.              */

            if (PrError() == 0) {           /* If no error occured...       */
                if (MaxBlock() < 10240) {   /* Enough memory to de-spool?   */
                    PurgeAll(MyID);         /* No - try to get some.        */
                    if (MaxBlock() < 10240) /* Enough memory now?           */

                        /* Not enough memory to de-spool to the ImageWriter */
                        /* so show a dialog box saying so. However, that    */
                        /* begs the question of whether or not we have      */
                        /* enough memory to show this dialog box...         */

                        AlertWindow(0,NULL,NoMemAlertString);
                        return;
                };
                PrPicFile(PrintHndl,NULL,&myStatusRec);
                CloseDialog(NowPrintingDialogPtr);  /* Remove status box.   */

            } else {                            /* If an error occurred...  */

                CloseDialog(NowPrintingDialogPtr);  /* Remove status box.   */
                ShowError(PrError());
            }
        }
    }
}


/* ************************************************************************* */
/*                                                                           */
/*  Draws just one part of the borders item. Called when part of the frame   */
/*  has been clicked on, or when by DrawUserItem when the whole thing needs  */
/*  to be updated.                                                           */
/*                                                                           */
/* ************************************************************************* */

void DrawLine(lineNum)
int lineNum;
{
    /* If the whole control is disabled, or the specific part of it that we */
    /* are drawing is disabled, set the pen pattern to grey. Otherwise, set */
    /* it to black */

    if ((myBorders[lineNum].used) && (myBorders[0].used)) {
        PenNormal();
    } else {
        SetPenPat(greyPat);
    }
    PaintRect(&myBorders[lineNum].line);
}


/* ************************************************************************* */
/*                                                                           */
/*  This routine draws my Custom Items. It is passed a pointer to the        */
/*  Dialog box and the number of the item to draw. It uses this item in a    */
/*  switch statement that draws all of the items. The Icon items are drawn   */
/*  with PPToPort, and the borders control is drawn by looping through and   */
/*  calling a routine that draws individual parts of the frame.              */
/*                                                                           */
/* ************************************************************************* */

pascal void DrawUserItem(theDialog,item)
GrafPortPtr theDialog;
int         item;
{
    int         i,w;
    LongWord    oldFont;

    SetPort((GrafPortPtr)theDialog);
    switch (item) {
        case Borders:
            for (i = 1; i < 8; DrawLine(i++));
            PenNormal();
            break;
        case Portrait:
            PPToPort(&portraitLoc,
                     &portraitRect,
                     portraitButton.itemRect.h1,
                     portraitButton.itemRect.v1,
                     modeCopy);
            if (!sideways) InvertRect(&portraitButton.itemRect);
            break;
        case Landscape:
            PPToPort(&landscapeLoc,
                     &landscapeRect,
                     landscapeButton.itemRect.h1,
                     landscapeButton.itemRect.v1,
                     modeCopy);
            if (sideways) InvertRect(&landscapeButton.itemRect);
            break;
    }
}


/* ************************************************************************* */
/*                                                                           */
/*  Filter procedure for my main Dialog box. This routine looks for Key      */
/*  Down events with the Command (Open-Apple) key down. If there is one, it  */
/*  looks to see if the key pressed is one that corresponds to a button. If  */
/*  so, then it tells the Dialog Manager that a hit occured on that button   */
/*  and it flashes that button. It then tells the Dialog Manager that the    */
/*  filter procedure handled the event, and that the Dialog Manager shouldnt */
/*  bother. If none of the above is true, then the Dialog Manager is told to */
/*  go ahead and handle the event.                                           */
/*                                                                           */
/*  mod 6/27: Changes Carriage Returns into TABs                             */
/*                                                                           */
/* ************************************************************************* */

pascal Word DialogFilterProc(theDialog, theEvent, theItem)
GrafPortPtr     theDialog;
EventRecord     *theEvent;
long            *theItem;

{
    CtlRecHndl  tHandle;
    char        ch;
    int         myItem;


    if ((*theEvent).what == keyDownEvt) {   /* Is this a Keydown event?     */
        ch = (*theEvent).message & 0x7f;    /* Yes, get the key into 'ch'   */
        if (((appleKey & (*theEvent).modifiers) != 0)) {    /* Apple down?  */
            /* Yes, see if the character pressed is special                 */
            myItem = 0;
            switch (ch) {
                case 'P':
                case 'p':
                    myItem = Print; /* cmd-P equivalent to pressing Print   */
                    break;
                case 'F':
                case 'f':
                    myItem = ChFile;/* cmd-F equivalent to pressing File    */
                    break;
                case 'Q':
                case 'q':
                    myItem = Quit;  /* cmd-Q equivalent to pressing Quit    */
                    break;
            }
            if (myItem != 0) {      /* Flash the button (if any)            */
                tHandle = GetControlDItem(theDialog,myItem);
                HiliteControl(1,tHandle);
                HiliteControl(0,tHandle);
                *theItem = myItem;
                return(true);       /* Tell Dialog manager we did something */
            }
        } else {
            switch (ch) {
                case 0x0D:
                    (*theEvent).message = 0x0009;   /* turn CRs into TABs   */
                    ch = 0x09;
                case 0x09:          /* update percent reduction if TAB hit  */
                    doPercent();
                    break;
            }
        }
    }
    return(false);                  /* Tell Dialog manager to handle it     */
}



/* ************************************************************************* */
/*                                                                           */
/* EventLoop - This routine calls Modal Dialog to handle all events. It      */
/* detects whether any items have been clicked on, and returns to us if any  */
/* have. Note how we pass a pointer to a filter procedure. This procedure is */
/* called by the dialog manager to handle events. In my case, I use it to    */
/* detect command key sequences, and to make them emulate clicks on certain  */
/* buttons. I also set bit 31 of the pointer to the filter procedure to tell */
/* the dialog manager to perform its own filtering of events if I opt not to */
/* do any of my own. See page 6-66 of the ToolBox Reference Manual for a     */
/* description of the standard filtering procedure.                          */
/*                                                                           */
/* ************************************************************************* */
EventLoop()
{
    word what;

    /* Call ModalDialog to handle events in a Dialog box I created in the   */
    /* InitApp section.                                                     */

    do {
        what = ModalDialog((long)DialogFilterProc | 0x80000000);
        SetPort((GrafPortPtr)myDialog);
        if (what != Percent) doPercent();   /* do on any event BUT Percent  */
        switch (what) {
            case About:             /* About button pressed */
                doAbout();
                break;
            case ChFile:            /* File button pressed */
                doChFile();
                break;
            case ChPrinter:         /* Choose Printer button pressed */
                doChPrinter();
                break;
            case ChFont:            /* Choose Font button pressed */
                doChFont();
                break;
            case PgSetup:           /* Page Setup button pressed */
                doPgSetup();
                break;
            case Print:             /* Print button pressed */
                doPrint();
                break;
            case UseBorder:         /* Use Border check box clicked on */
                doCheckBox();
                break;
            case Borders:           /* Borders User Item clicked on */
                doBorders();
                break;
            case Columns:           /* Event in Columns EditLine item */
                doColumns();
                break;
            case Landscape:         /* Click on Portrait or Landscape icons */
            case Portrait:
                doPrDirection(what);
                break;
        } /* end switch */
    } while (what != Quit);
}
